<template>
  <split-pane
    :min-percent="75"
    :default-percent="75"
    :max-percent="75"
    split="horizontal"
    v-loading="loading"
    element-loading-text="视频解析模块正在加载中，请稍等..."
    element-loading-spinner="el-icon-loading"
    element-loading-background="rgba(0, 0, 0, 0.8)"
  >
    <template slot="paneL">
      <split-pane split="vertical" :default-percent="70">
        <template slot="paneL">
          <div id="videoPanel" @drop="dropToTrash" @dragover.prevent>
            <video id="markedVideo" height="100%" width="0">
              <source src type="video/mp4" />
            </video>
            <div id="canvasWrap">
              <canvas id="canvas"></canvas>
            </div>
          </div>
          <div id="controllBar">
            <el-button icon="iconfont iconhoutui" type="info" @click="seekFrames(-1)" circle></el-button>
            <el-button icon="iconfont iconqianjin" type="info" @click="seekFrames(1)" circle></el-button>
            <el-button icon="iconfont iconkaishi" type="info" @click="playOrPause" circle></el-button>
            <el-slider
              @change="frameChange"
              id="currentFrameBar"
              v-model="currentFrameNum"
              :min="1"
              :max="parseInt(this.videoinfo.frameCount.value || '1')"
            ></el-slider>
            <span
              id="currentFrameSpan"
            >{{this.videoinfo.currentFrameNum.value || "1"}}/{{this.videoinfo.frameCount.value || "1"}}</span>
            <el-dropdown @command="handleSpeedCommand">
              <el-button id="speedBtn" type="info" round>速度：1x</el-button>
              <el-dropdown-menu slot="dropdown">
                <el-dropdown-item command="1.5">1.5x</el-dropdown-item>
                <el-dropdown-item command="1.25">1.25x</el-dropdown-item>
                <el-dropdown-item command="1">正常</el-dropdown-item>
                <el-dropdown-item command="0.75">0.75x</el-dropdown-item>
                <el-dropdown-item command="0.5">0.5x</el-dropdown-item>
                <el-dropdown-item command="0.25">0.25x</el-dropdown-item>
              </el-dropdown-menu>
            </el-dropdown>
            <!-- <el-popover placement="top" trigger="hover" width="40px" popper-class="min-poper">
              <el-slider v-model="volume" vertical height="100px"></el-slider>
              <el-button slot="reference" icon="iconfont iconshengyin" type="info" circle></el-button>
            </el-popover> -->
            <span class="ctrolbar-font">模式:</span>
            <el-select v-model="markMode" placeholder="请选择标记模式" style="width: 120px">
              <el-option
                v-for="item in markModelOption"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              ></el-option>
            </el-select>
          </div>
        </template>
        <template slot="paneR">
          <div style id="right-info-panel">
            <div class="infotitle">视频信息</div>
            <input
              id="fileSelect"
              type="file"
              @change="fileSelectedChange"
              name="file"
              class="inputfile"
            />
            <label for="fileSelect">选择文件</label>
            <el-table :show-header="false" :data="videoInfoTable" :cell-style="{padding: 0}">
              <el-table-column width="80">
                <template slot-scope="scope">
                  <span class="video-info-colume">{{ scope.row.name }}</span>
                </template>
              </el-table-column>
              <el-table-column>
                <template slot-scope="scope">
                  <span class="video-info-colume">{{ scope.row.value }}</span>
                </template>
              </el-table-column>
            </el-table>
            <div class="infotitle">当前帧标记信息</div>
            <el-table
              :data="currentMarkLocation"
              :cell-style="{padding: 0}"
            >
              <el-table-column label="轨迹" width="50">
                <template slot-scope="scope">
                  <span class="video-info-colume" @click="changeTrailName(scope.row)">{{ scope.row.trailName }}</span>
                </template>
              </el-table-column>
              <el-table-column label="X坐标">
                <template slot-scope="scope">
                  <input
                    type="number"
                    v-model="scope.row.X"
                    @change="handlePointChange(scope.row)"
                    class="positionInfo"
                  />
                </template>
              </el-table-column>
              <el-table-column label="Y坐标">
                <template slot-scope="scope">
                  <input
                    type="number"
                    v-model="scope.row.Y"
                    @change="handlePointChange(scope.row)"
                    class="positionInfo"
                  />
                </template>
              </el-table-column>
              <el-table-column label="宽">
                <template slot-scope="scope">
                  <input
                    type="number"
                    v-model="scope.row.width"
                    @change="handlePointChange(scope.row)"
                    class="positionInfo"
                  />
                </template>
              </el-table-column>
              <el-table-column label="高">
                <template slot-scope="scope">
                  <input
                    type="number"
                    v-model="scope.row.height"
                    @change="handlePointChange(scope.row)"
                    class="positionInfo"
                  />
                </template>
              </el-table-column>
              <el-table-column label="操作">
                <template slot-scope="scope">
                  <el-button
                    size="mini"
                    type="danger"
                    icon="el-icon-delete"
                    circle
                    @click="deleteSinglePoint(scope.row)"
                  ></el-button>
                </template>
              </el-table-column>
            </el-table>
            <div id="infoPanelBtnWrap">
              <el-button type="success" round @click="completeMark">标记完成</el-button>
              <el-button type="danger" round @click="reMark">重新标记</el-button>
            </div>
          </div>
        </template>
      </split-pane>
    </template>
    <template slot="paneR">
      <div id="bottomTrail">
        <div
          v-for="(trial,trailIndex) in displayTrail"
          class="trailContainer"
          v-bind:key="trailIndex"
          @drop="dropToAdd($event, trailIndex)"
          @dragover.prevent
        >
          <div
            v-for="(para,index) in trial"
            v-bind:key="index"
            class="trial"
            :style="{left: para.left, right: para.right}"
            :id="trailIndex + '-' + index"
            draggable="true"
            @dragstart="drag($event, para)"
          ></div>
        </div>
        <div id="ruler-container">
          <div
            v-for="count in frameRuler"
            v-bind:key="count"
            class="ruler"
            :style="{width: 100/videoinfo.frameCount.value+'%'}"
          ></div>
        </div>
      </div>
    </template>
  </split-pane>
</template>

<script>
import { fabric } from "fabric";
import $ from "jquery";
import BMF from "browser-md5-file";
import FrameTool from "../tools/frame-calculator";
import md5 from "js-md5";

export default {
  data() {
    return {
      inputMode: false, //给轨迹命名的时候按键与标记使用的快捷键冲突
      moniterFrameTask: null,
      defaultObstacleSize: 50,
      createdRect: { left: 0, top: 0, width: 0, height: 0, creating: false },
      trailChanges: 0,
      loading: true,
      markModelOption: [
        { label: "播放标记", value: 1 },
        { label: "分帧调整", value: 2 },
        { label: "分帧标记", value: 3 }
      ],
      markMode: 1,
      obstacleNum: 0,
      currentCoverObject: null,
      markinfo: [],
      currentMouseCoordinate: { X: 0, Y: 0 },
      trackMouseTask: null,
      canvas: null,
      volume: 100,
      currentFrameNum: 1,
      MediaInfoModule: null,
      selectedFile: null,
      videoinfo: {
        filename: { name: "文件名", value: null },
        width: { name: "宽度(pix)", value: null },
        height: { name: "高度(pix)", value: null },
        duration: { name: "时长(ms)", value: null },
        fps: { name: "帧率", value: null },
        frameCount: { name: "总帧数", value: null },
        currentFrameNum: { name: "当前帧", value: null },
        md5: { name: "MD5", value: null },
        screenHeight: { name: "播放高度", value: null },
        screenWidth: { name: "播放宽度", value: null }
      },
      video: null,
      bmf: null
    };
  },
  methods: {
    getUserName: function(){
      //从cookies中获取用户名
      return "lukeaxu";
    },
    scalePosition: function(before, referBefore, referNow) {
      return Math.round((referNow * before) / referBefore);
    },
    addToken: function(o) {
      o.auth.timestamp = "" + new Date().getTime();
      o.auth.token = md5("root" + o.auth.timestamp + "root");
      return o;
    },
    transformDataStruct: function(rawData) {
      let result = {
        auth: {
          timestamp: null,
          token: null
        },
        videoSignArray: []
      };
      this.addToken(result);
      rawData.forEach(trail => {
        let videoSign = {};
        //轨道名称
        videoSign.sign = "" + trail.name;
        //md5作为视频的名称
        videoSign.videoName = this.videoinfo.md5.value;
        videoSign.videoHeight = +this.videoinfo.height.value;
        videoSign.videoWidth = +this.videoinfo.width.value;
        videoSign.videoSignDetail = [];
        trail.points.forEach(point => {
          //上报只需要中心点的位置
          let detail = {};
          detail.frame = +point.frameNum;
          //point.x是相对视频播放时的像素，上部时候转为视频的实际像素
          detail.locationX = this.scalePosition(
            point.x,
            this.videoinfo.screenWidth.value,
            this.videoinfo.width.value
          );
          //point.y是相对视频播放时的像素，上部时候转为视频的实际像素
          detail.locationY = this.scalePosition(
            point.y,
            this.videoinfo.screenHeight.value,
            this.videoinfo.height.value
          );
          //todo 获取用户名
          detail.updateBy = this.getUserName();
          videoSign.videoSignDetail.push(detail);
        });
        result.videoSignArray.push(videoSign);
      });
      return result;
    },
    changeTrailName: function(point) {
      this.inputMode = true;
      this.$prompt("请命名轨迹", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        inputPattern:  /^[a-zA-Z0-9\/]{1,15}$/,
        inputErrorMessage: "格式不正确"
      })
        .then(({ value }) => {
          this.inputMode = false;
          point.trail.name = value;
          point.trailName = value;
          this.$message({
            type: "success",
            message: "轨迹名称是: " + value
          });
        })
        .catch(() => {
          this.inputMode = false;
          this.$message({
            type: "info",
            message: "取消输入"
          });
        });
    },
    movedCheck: function() {
      //检查移动段落后是否产生了空轨迹
      this.markinfo.forEach(trail => {
        let shouldDelete = true;
        trail.points.forEach(point => {
          if (point) {
            shouldDelete = false;
          }
        });
        if (shouldDelete) {
          this.deleteTrail(trail);
        }
      });
    },
    deleteTrail: function(trail) {
      //删除canvas中的图形
      this.canvas.remove(trail.obstacle);
      //将元素从markinfo中pop出来
      this.markinfo.splice(this.markinfo.indexOf(trail), 1);
    },
    completeMark: function() {
      //把markinfo中标记的轨迹信息转换为后台需要的形式
      let formatData = this.transformDataStruct(this.markinfo);
      //上传到服务器
      this.$api.post(
        "http://platform.test.check.wsd.com/addVideoSign",
        formatData,
        res => {
          console.log(res);
          this.$message({
            message: "保存成功！",
            type: "success"
          });
        }
      );
    },
    reMark: function() {
      this.initTrailMark();
      this.canvas.clear();
    },
    showMessage: function(tipContent) {
      //弹出框提示
      this.$alert(tipContent, "提示", {
        confirmButtonText: "确定",
        callback: action => {}
      });
    },
    drag: function(event, paragraph) {
      //拖动一个标记段落
      //段落(div)的id命名: 轨道序号-段落序号
      //传递参数，告知是哪个一个轨道的哪一个段落被拖动，段落的起止[start, end]
      event.dataTransfer.setData("start", paragraph.start);
      event.dataTransfer.setData("end", paragraph.end);
      event.dataTransfer.setData("trailIndex", event.target.id.split("-")[0]);
    },
    dropToAdd: function(event, aimTrail) {
      //拖动段落到指定的轨道上释放
      let startFrame = parseInt(event.dataTransfer.getData("start"));
      let endFrame = parseInt(event.dataTransfer.getData("end"));
      let srcTrail = parseInt(event.dataTransfer.getData("trailIndex"));
      //检查拖动段落与轨道上的标记是否有重叠
      for (let i = startFrame; i <= endFrame; i++) {
        if (this.markinfo[aimTrail].points[i]) {
          this.showMessage("标记区域有重叠，请裁剪后执行合并");
          return;
        }
      }
      //如果没有重叠则合并段落到轨道
      for (let i = startFrame; i <= endFrame; i++) {
        this.markinfo[aimTrail].points[i] = this.markinfo[srcTrail].points[i];
        this.markinfo[srcTrail].points[i] = undefined;
      }
      //计算是否因为移动产生了空轨道
      this.movedCheck();
      //触发compute属性计算，更新轨道栏显示
      this.trailChanges++;
      event.preventDefault();
    },
    dropToTrash: function(event) {
      //删除轨道上从[start, end]间的点
      let startFrame = parseInt(event.dataTransfer.getData("start"));
      let endFrame = parseInt(event.dataTransfer.getData("end"));
      let srcTrail = parseInt(event.dataTransfer.getData("trailIndex"));
      //检查拖动段落与轨道上的标记是否有重叠
      for (let i = startFrame; i <= endFrame; i++) {
        this.markinfo[srcTrail].points[i] = undefined;
      }
      //计算是否因为移动产生了空轨道
      this.movedCheck();
      //触发compute属性计算，更新轨道显示
      this.trailChanges++;
      event.preventDefault();
    },
    windowSizeChanged: function() {
      //记录当前的（改变前的）的canvas宽高
      let widthBefore = this.canvas.getWidth();
      let heightBefore = this.canvas.getHeight();
      //获取video位置，重新定位canvas并清空，（todo 与现有函数合并？）
      this.calCanvasPosition();
      //处理已经记录的位置点的信息
      this.reAddPoint(widthBefore, heightBefore);
    },
    deleteSinglePoint: function(point) {
      //找到轨道
      let trail = point.trail;
      if (trail) {
        //删除轨道上的这一点
        trail.points[point.frameNum] = undefined;
      } else {
        console.error("没有找到轨迹点所属的轨道");
      }
      //更新图形
      this.drawObstacle();
      this.trailChanges++;
    },
    handlePointChange: function(point) {
      //找到轨道
      let trail;
      this.markinfo.forEach(aTrail => {
        if (aTrail.name == point.trailName) {
          trail = aTrail;
        }
      });
      if (trail) {
        trail.points[point.frameNum].x = parseInt(point.X);
        trail.points[point.frameNum].y = parseInt(point.Y);
        // trail.points[point.frameNum].size = parseInt(point.size);
        trail.points[point.frameNum].width = parseInt(point.width);
        trail.points[point.frameNum].height = parseInt(point.height);

        for (let i = point.frameNum; i < trail.points.length; i++) {
          if (trail.points[i]) {
            // trail.points[i].size = parseInt(point.size);
            trail.points[i].width = parseInt(point.width);
            trail.points[i].height = parseInt(point.height);
          }
        }
      } else {
        console.error("没有找到轨迹点所属的轨道");
      }
      //更新图形
      this.drawObstacle();
    },
    polishMark: function(trail) {
      //播放标记中间可能会出现断帧的现象，此方法补齐中间的断帧。拖拽合并时候不调用此方法
      //当前帧从1开始数[1, this.videoinfo.frameCount.value]
      let lastPointIndex;
      for (let i = 1; i < trail.points.length; i++) {
        if (!lastPointIndex) {
          if (trail.points[i]) {
            //记录整个轨道的第一个标记帧
            lastPointIndex = i;
          }
        } else {
          if (trail.points[i]) {
            if (i - lastPointIndex > 1) {
              //当前帧与上一个标记帧间隔大于1，表示补齐中间缺失的帧
              let deltaX =
                (trail.points[i].x - trail.points[lastPointIndex].x) /
                (i - lastPointIndex);
              let deltaY =
                (trail.points[i].y - trail.points[lastPointIndex].y) /
                (i - lastPointIndex);
              let deltaT =
                (trail.points[i].time - trail.points[lastPointIndex].time) /
                (i - lastPointIndex);
              for (let j = lastPointIndex + 1; j < i; j++) {
                trail.points[j] = {
                  frameNum: j,
                  time: trail.points[j - 1].time + deltaT,
                  x: Math.round(trail.points[j - 1].x + deltaX),
                  y: Math.round(trail.points[j - 1].y + deltaY),
                  // size: trail.points[j - 1].size,
                  //拖拽记录的过程不会产生width和height的改变
                  //这里直接沿用上一个点的宽高
                  width: trail.points[j - 1].width,
                  height: trail.points[j - 1].height
                };
              }
            }
            //更新lastPositnIndex
            lastPointIndex = i;
          }
        }
      }
    },
    recordPosition: function(trail) {
      //鼠标拖拽过程中记录轨迹点
      //这里不用this.currentFrameNum是因为currentFrameNum更新有延迟，这里希望获得准确的当前帧的序号
      let frameNum = FrameTool.secondsToFrameNum(
        this.video.currentTime,
        this.videoinfo.fps.value
      );
      if (!trail) {
        //如果没有参数trail传入（播放标记模式下的定时任务）则取最后一条轨迹为trail
        trail = this.markinfo[this.markinfo.length - 1];
      }
      if (trail.points[frameNum]) {
        //如果获取了同一帧，以后者获取为准。更新time和x，y
        trail.points[frameNum].time = this.video.currentTime;
        trail.points[frameNum].x = this.currentMouseCoordinate.X;
        trail.points[frameNum].y = this.currentMouseCoordinate.Y;
      } else {
        trail.points[frameNum] = {};
        trail.points[frameNum].frameNum = frameNum;
        trail.points[frameNum].time = this.video.currentTime;
        //记录rect图形的中间位置
        trail.points[frameNum].x = this.currentMouseCoordinate.X;
        trail.points[frameNum].y = this.currentMouseCoordinate.Y;
        //todo size将要删掉
        // trail.points[frameNum].size = this.defaultObstacleSize;
        //默认的rect大小（包括宽高）
        trail.points[frameNum].width = this.defaultObstacleSize;
        trail.points[frameNum].height = this.defaultObstacleSize;
      }
    },
    markToEndFrame: function(trail, frameNum, rect) {
      //从frameNum取出标记物，创建段落用相同的标记物（位置，大小）标记直到最后一帧
      let totalFrameCount = parseInt(this.videoinfo.frameCount.value);
      //计算标记物的中心位置
      let width = rect.width;
      let height = rect.height;
      let x = rect.left + width / 2;
      let y = rect.top + height / 2;
      let size = rect.width;
      for (let i = frameNum; i <= totalFrameCount; i++) {
        trail.points[i] = { time: null, frameNum: i, x, y, size, width, height};
      }
    },
    createNewObstacleTrail: function(name) {
      //检查命名不重复，创建一个命名轨道
      let trailName = name ? name : this.obstacleNum++;
      let exist = this.markinfo.filter(trail => {
        return trail.name == trailName;
      }).length;
      if (exist == 0) {
        //命名不重复
        let trail = { name: trailName, points: [] };
        this.markinfo.push(trail);
        return trail;
      }
    },
    createRectForTrail: function(trail, isDrag) {
      //为轨迹创建一个图形,图形的中心在鼠标currentMouseCoordinate的位置
      //调用此方法前应该更新currentMouseCoordinate的值
      let generateRandomColor = () => {
        return "rgba(255," + 
          this.generateRandomInt(0,255) + "," + 
          this.generateRandomInt(0,255) + ", 0.2)";
      };
      let option = {
        left:
          this.currentMouseCoordinate.X -
          Math.round(this.defaultObstacleSize / 2),
        top:
          this.currentMouseCoordinate.Y -
          Math.round(this.defaultObstacleSize / 2),
        fill: generateRandomColor(),
        width: this.defaultObstacleSize,
        height: this.defaultObstacleSize,
        selection: true
      };
      if (isDrag != undefined && isDrag) {
        //如果是拖拽，则拖拽记录的left，width等属性会覆盖默认的形状大小
        Object.assign(option, this.createdRect);
      }
      let rect = new fabric.Rect(option);
      this.canvas.add(rect);
      this.canvas.setActiveObject(rect);
      //图形与轨迹相互绑定
      rect.trailName = trail.name;
      rect.trail = trail;
      trail.obstacle = rect;
      return rect;
    },
    initCanvasMarkLogic: function() {
      var that = this;
      this.canvas.on("mouse:down", function(e) {
        //获取并记录当前鼠标位置
        let X = e.pointer.x;
        let Y = e.pointer.y;
        that.currentMouseCoordinate.X = Math.round(X);
        that.currentMouseCoordinate.Y = Math.round(Y);
        if (that.markMode == 1) {
          //如果是标记播放模式，
          that.marking = true;
          //创建物体轨迹对象
          let obstacle = that.createNewObstacleTrail();
          //添加定时任务
          that.recordPosition(obstacle);
          var _that = that;
          that.trackMouseTask = setInterval(
            that.recordPosition,
            Math.round(parseInt(that.videoinfo.fps.value) / 2)
          );
          if (that.video.paused) {
            that.video.play();
          }
        }
        if (that.markMode == 3) {
          //准备创建新图形(拖拽添加图形的时候需要记录，quickclick创建默认图形不使用这个对象)
          that.createdRect.creating = true;
          that.createdRect.left = that.currentMouseCoordinate.X;
          that.createdRect.top = that.currentMouseCoordinate.Y;
        }
      });
      this.canvas.on("mouse:move", function(e) {
        if (that.markMode == 1) {
          //如果是标记播放模式，鼠标移动更新移动位置。
          //此时追踪轨迹的任务正在定时调度
          that.currentMouseCoordinate.X = Math.round(e.pointer.x);
          that.currentMouseCoordinate.Y = Math.round(e.pointer.y);
        }
      });
      this.canvas.on("mouse:up", function(e) {
        if (that.markMode == 1) {
          //如果是标记播放模式
          //轨迹追踪任务结束（结束前需要手动调用记录点的位置一次），暂停播放
          clearInterval(that.trackMouseTask);
          if (that.video.played) {
            that.video.pause();
          }
          let obstacle = that.markinfo[that.markinfo.length - 1];
          that.recordPosition(obstacle);
          //处理刚刚添加轨迹的断帧问题
          that.polishMark(obstacle);
          that.createRectForTrail(obstacle);
          that.trailChanges++;
        }
        if ((that.markMode == 3) & that.createdRect.creating) {
          //判定是拖拽还是快速点击一次
          let X = e.pointer.x;
          let Y = e.pointer.y;
          //在此模式下，此时currentMouseCoordinate代表鼠标点击下去时候的位置
          let pathLength = Math.sqrt(
            Math.pow(X - that.currentMouseCoordinate.X, 2) +
              Math.pow(Y - that.currentMouseCoordinate.Y, 2)
          );
          let quickClick = pathLength < 50;
          let trail = that.createNewObstacleTrail();
          let rect;
          if (quickClick) {
            //创建默认的图形大小，图形的中心在鼠标位置
            that.currentMouseCoordinate.X = X;
            that.currentMouseCoordinate.Y = Y;
            rect = that.createRectForTrail(trail);
          } else {
            //根据拖拽的起始位置创建图形
            that.createdRect.width = Math.round(X - that.createdRect.left);
            that.createdRect.height = Math.round(Y - that.createdRect.top);
            //根据drag创建标记物
            rect = that.createRectForTrail(trail, true);
          }
          //创建轨道，填充此后所以帧
          that.markToEndFrame(trail, that.currentFrameNum, rect);
          that.trailChanges++;
        }
      });
      this.canvas.on("object:modified", function(e) {
        //只允许在“分帧调整”模式下使用鼠标调整图形的位置和大小
        //其他模式使用输入值得方式
        if (that.markMode == 2) {
          let obstacle = e.target;
          let size = obstacle.width * obstacle.scaleX;
          //X: 图形中心的位置的x坐标值 Y: 图形中心的y坐标值
          let width = obstacle.width * obstacle.scaleX;
          let height = obstacle.height * obstacle.scaleY;
          let point = {
            trail: e.target.trail,
            trailName: e.target.trail.name,
            X: obstacle.left + width / 2,
            Y: obstacle.top + height / 2,
            //缩放图形后的图形的宽高计算方式，大小以宽度为准
            size,
            width,
            height,
            frameNum: that.currentFrameNum
          };
          //恢复缩放比例
          obstacle.scaleX = 1;
          obstacle.scaleY = 1;
          //处理图形改变的逻辑
          that.handlePointChange(point);
        }
      });
    },
    getUrlFromLocalFile: function(file) {
      if (window.createObjectURL != undefined) {
        return window.createObjectURL(file);
      }
      if (window.URL != undefined) {
        return window.URL.createObjectURL(file);
      }
      if (window.webkitURL != undefined) {
        return window.webkitURL.createObjectURL(file);
      }
    },
    setVideoSrcToLocalFile: function(file) {
      //上传文件后，设置video元素的src属性
      this.video.src = this.getUrlFromLocalFile(file);
    },
    refreshVideoInfo: function(file) {
      let that = this;
      //上传文件后，调用WASM模块获取视频的信息
      //获取模块实例对象
      const MI = new this.MediaInfoModule.MediaInfo();
      MI.Open(file, function() {
        let fps = MI.Get(that.MediaInfoModule.Stream.Video, 0, "FrameRate");
        let width = MI.Get(that.MediaInfoModule.Stream.Video, 0, "Width");
        let height = MI.Get(that.MediaInfoModule.Stream.Video, 0, "Height");
        let duration = MI.Get(that.MediaInfoModule.Stream.Video, 0, "Duration");
        let filename = MI.Get(
          that.MediaInfoModule.Stream.General,
          0,
          "CompleteName"
        );
        let frameCount = MI.Get(
          that.MediaInfoModule.Stream.Video,
          0,
          "FrameCount"
        );
        that.videoinfo.fps.value = fps;
        that.videoinfo.width.value = width;
        that.videoinfo.height.value = height;
        that.videoinfo.duration.value = duration;
        that.videoinfo.filename.value = filename;
        that.videoinfo.frameCount.value = frameCount;
        MI.Close();
      });
      //计算视频MD5
      const bmf = new BMF();
      bmf.md5(file, (err, md5) => {
        that.videoinfo.md5.value = md5;
      });
    },
    initTrailMark: function() {
      //清空轨迹记录
      this.markinfo = [];
      //刷新轨迹栏的显示
      this.trailChanges = 0;
    },
    fileSelectedChange: function(e) {
      let fileSelected = $("#fileSelect")[0]; //获取id
      let file = fileSelected.files[0];

      //添加播放视频链接
      this.setVideoSrcToLocalFile(file);
      //更新展示的视频信息
      this.refreshVideoInfo(file);
      //初始化/清空轨迹信息
      this.initTrailMark();
    },
    initFrameProgressPollTask: function() {
      //定时任务，不断获取当前帧的序号，从1开始记录
      var that = this;
      if (this.moniterFrameTask) {
        //如果有任务则停止
        clearInterval(this.moniterFrameTask);
      }
      this.moniterFrameTask = setInterval(function() {
        if (that.video.src) {
          that.currentFrameNum = FrameTool.secondsToFrameNum(
            that.video.currentTime,
            that.videoinfo.fps.value
          );
        }
      }, 50);
    },
    frameChange: function(seekFrameNum) {
      //调整上一帧和下一帧
      if (this.video) {
        this.video.currentTime = FrameTool.frameNumToSeconds(
          seekFrameNum,
          this.videoinfo.fps.value
        );
      }
    },
    seekFrames: function(nr_of_frames) {
      if (this.video.src) {
        this.video.pause();
        let nextFrame = this.currentFrameNum + nr_of_frames;
        if (nextFrame > this.videoinfo.frameCount.value || nextFrame < 1) {
          //超出范围，do nothing
        } else {
          this.video.currentTime = FrameTool.frameNumToSeconds(
            nextFrame,
            this.videoinfo.fps.value
          );
        }
      }
    },
    playOrPause: function() {
      //播放或暂停播放视频
      this.video.paused ? this.video.play() : this.video.pause();
    },
    handleSpeedCommand: function(speed) {
      //调整视频的播放速度
      if (this.video) {
        this.video.playbackRate = speed;
        $("#speedBtn")[0].innerText = "速度：" + speed + "x";
      }
    },
    getObstaclePositon: function(trail, frameNum) {
      //计算标记物的显示位置
      let newPositon = {};
      if (trail.points[frameNum]) {
        newPositon.left =
          trail.points[frameNum].x - trail.points[frameNum].width / 2;
        newPositon.top =
          trail.points[frameNum].y - trail.points[frameNum].height / 2;
        newPositon.width = trail.points[frameNum].width;
        newPositon.height = trail.points[frameNum].height;
      } else {
        //移动到画布外，隐藏图形,另一种方案opacity = 0 备选
        newPositon.left = -1000;
        newPositon.top = -1000;
      }
      return newPositon;
    },
    drawObstacle: function() {
      //根据markinfo中的记录，更新标记物的位置
      let frameNum = this.currentFrameNum;
      for (var i = 0; i < this.markinfo.length; i++) {
        let trail = this.markinfo[i];
        if (trail.obstacle) {
          var newPosition = this.getObstaclePositon(trail, frameNum);
          trail.obstacle.set(newPosition).setCoords();
        }
      }
      //如果有标记物，则刷新显示
      if (this.canvas && this.markinfo.length > 0) {
        this.canvas.renderAll();
      }
    },
    calCanvasPosition: function() {
      //重新定位canvas位置并且清空其中的内容
      let marginLeft = (-1 * this.video.clientWidth) / 2 + "px";
      $("#markedVideo").css("margin-left", marginLeft);
      $("#canvasWrap").css("margin-left", marginLeft);
      //每一次载入视频都从新建立画布
      if(!this.canvas){
        this.canvas = new fabric.Canvas("canvas");
      }
      this.canvas.selection = false;
      this.canvas.setWidth(this.video.clientWidth);
      this.canvas.setHeight(this.video.clientHeight);
      //记录播放视频实际呈现的宽高
      this.videoinfo.screenHeight.value = this.video.clientHeight;
      this.videoinfo.screenWidth.value = this.video.clientWidth;
    },
    reAddPoint: function(widthBefore, heightBefore){
      this.canvas.clear();
      this.markinfo.forEach(trail => {
        trail.points.forEach(point => {
          point.x = this.scalePosition(point.x, widthBefore,this.videoinfo.screenWidth.value);
          point.y = this.scalePosition(point.y, heightBefore,this.videoinfo.screenHeight.value);
          point.width = this.scalePosition(point.width, widthBefore, this.videoinfo.screenWidth.value);
          point.height = this.scalePosition(point.height, heightBefore, this.videoinfo.screenHeight.value)
        });
      });
      this.markinfo.forEach(trail => {
        this.canvas.add(trail.obstacle);
      });
    },
    generateRandomInt: function(from, to){
      return Math.round(from + (to-from) * Math.random());
    }
  },
  watch: {
    currentFrameNum: function(val, oldval) {
      //currentFrameNum改变是因为有一个定时任务在不断调度
      //更新videoinfo中记录的当前帧（暂时还么有用到）
      this.videoinfo.currentFrameNum.value = val;
      //绘制当前帧的标记物
      this.drawObstacle();
    },
    volume: function(val, oldval) {
      //修改音量
      if (this.video) {
        this.video.volume = parseInt(val) / 100;
      }
    }
  },
  computed: {
    videoInfoTable: function() {
      //转换videoinfo到表格需要的数据结构
      let tableInfo = [];
      Object.keys(this.videoinfo).forEach(attribute => {
        tableInfo.push(this.videoinfo[attribute]);
      });
      return tableInfo;
    },
    currentMarkLocation: function() {
      //显示当前帧标记的点的信息
      var result = [];
      var that = this;
      this.trailChanges;
      this.markinfo.forEach(trail => {
        let point = trail.points[this.currentFrameNum];
        if (point) {
          result.push({
            trail,
            trailName: trail.name,
            X: point.x,
            Y: point.y,
            width: point.width,
            height: point.height,
            // size: point.size,
            frameNum: point.frameNum
          });
        }
      });
      return result;
    },
    frameRuler: function() {
      //显示标尺
      let result = [];
      for (let i = 0; i < this.videoinfo.frameCount.value; i++) {
        result.push(i);
      }
      return result;
    },
    displayTrail: function() {
      //更新显示的轨道，区分段落
      let that = this;
      let result = [];
      this.trailChanges;
      this.markinfo.forEach((trail, index) => {
        let startFrame = null;
        let points = trail.points;
        result.push([]);
        for (let i = 0; i <= points.length; i++) {
          if (!startFrame) {
            // 如果start为null
            if (points[i]) {
              // 如果当前不为null
              startFrame = i;
            }
          } else {
            if (!points[i]) {
              let left =
                (100 * startFrame) / that.videoinfo.frameCount.value + "%";
              let right = 100 * (1 - i / that.videoinfo.frameCount.value) + "%";
              //start 表示段落标记的首帧， end 表示段落标记的尾帧
              result[index].push({
                start: startFrame,
                end: i - 1,
                left,
                right
              });
              startFrame = null;
            }
          }
        }
      });
      return result;
    }
  },
  mounted: function() {
    window.onresize = this.windowSizeChanged;
    //绑定Dom元素
    var that = this;
    this.video = $("#markedVideo")[0];
    this.video.addEventListener("loadedmetadata", function(e) {
      $("#markedVideo")[0].setAttribute("width", "");
      //等待视频加载后自适应宽度，
      //否则clientWidth获取不到，将导致视频不能居中
      setTimeout(function() {
        let marginLeft = (-1 * that.video.clientWidth) / 2 + "px";
        $("#markedVideo").css("margin-left", marginLeft);
        $("#canvasWrap").css("margin-left", marginLeft);
        //每一次载入视频都从新建立画布
        that.canvas = new fabric.Canvas("canvas");
        that.canvas.selection = false;
        that.canvas.setWidth(that.video.clientWidth);
        that.canvas.setHeight(that.video.clientHeight);
        //初始化Canvas的标记逻辑
        that.initCanvasMarkLogic();
        //初始化标记需要存储的数据结构
        that.initTrailMark();
        //初始化帧选择进度条同步当前的播放时间
        that.initFrameProgressPollTask();
        //记录播放视频实际呈现的宽高
        that.videoinfo.screenHeight.value = that.video.clientHeight;
        that.videoinfo.screenWidth.value = that.video.clientWidth;
      }, 1000);
    });
    //加载WASM模块
    this.MediaInfoModule = window.MediaInfoLib({
      postRun: () => {
        // console.log("MediaInfoModule Ready")
        that.loading = false;
      }
    });
  },
  created: function() {
    var that = this;
    //绑定按键
    document.onkeydown = function(e) {
      let key = window.event.keyCode;
      if(!that.inputMode){
        if (key == 65 || key == 97) {
          // a || A 回退到上一帧
          that.seekFrames(-1);
        }
        if (key == 68 || key == 100) {
          // d || D 前进到下一帧
          that.seekFrames(1);
        }
        if (key == 81 || key == 113) {
          // q || Q 选择播放标记模式 1
          that.markMode = 1;
        }
        if (key == 87 || key == 119) {
          // w || W 选择分帧调整模式 2
          that.markMode = 2;
        }
        if (key == 69 || key == 101) {
          // e || e 选择分帧标记模式 3
          that.markMode = 3;
        }
        if (key == 83 || key == 115 || key == 32) {
          // s || S || 空格 播放暂停
          that.playOrPause();
        }
        if (key == 127) {
          // delete删除帧、段落、轨道逻辑
          //todo 删除逻辑
        }
      }
    };
  }
};
</script>

<style scoped>
#right-info-panel {
  width: 100%;
  height: 100%;
  overflow: scroll;
  margin-left: 2px;
}
.ctrolbar-font {
  color: #fff;
  padding-right: 10px;
  padding-left: 10px;
}
.ruler {
  display: inline-block;
  height: 10px;
  border-right: #fff solid 1px;
}
.ruler:nth-child(5n + 1) {
  height: 20px;
}
#videoPanel {
  position: absolute;
  top: 0;
  bottom: 50px;
  left: 0;
  right: 0;
}
.video-info-colume {
  font-size: 10px;
}
#currentFrameSpan {
  color: #fff;
  padding-right: 20px;
  width: 50px;
}
#currentFrameBar {
  display: inline-block;
  width: 150px;
  vertical-align: middle;
  padding-left: 20px;
  padding-right: 20px;
}
#markedVideo,
#canvasWrap {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  right: 0;
}
#layer {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 40%;
  right: 0;
}
#controllBar {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 2px;
  background-color: #000;
  overflow: hidden;
  height: 50px;
  line-height: 50px;
}
.infotitle {
  color: #000;
  font: 1.2em bold;
  line-height: 2em;
}
.inputfile {
  width: 0.1px;
  height: 0.1px;
  opacity: 0;
  overflow: hidden;
  position: absolute;
  z-index: -1;
}
.inputfile + label {
  font-size: 1em;
  font-weight: 700;
  color: white;
  background-color: black;
  display: inline-block;
  margin-left: 10px;
}
.inputfile:focus + label,
.inputfile + label:hover {
  background-color: blue;
}
.inputfile + label {
  cursor: pointer; /* "hand" cursor */
}
.inputfile:focus + label {
  outline: 1px dotted #000;
  outline: -webkit-focus-ring-color auto 5px;
}
.inputfile + label * {
  pointer-events: none;
}
.positionInfo {
  width: 60px;
}
#infoPanelBtnWrap {
  text-align: center;
  position: absolute;
  bottom: 0;
  height: 50px;
  left: 0;
  right: 0;
  background-color: #808080;
  margin-left: 4px;
  line-height: 50px;
  overflow: hidden;
}
#bottomTrail {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  background-color: #111122;
  overflow: scroll;
  position: relative;
}
.trailContainer {
  width: 100%;
  height: 50px;
  border: #2f4f4f solid 2px;
  position: relative;
}
.trial {
  display: inline-block;
  position: absolute;
  left: 10px;
  right: 900px;
  background-color: rgb(37, 73, 73);
  height: 50px;
}
</style>

<style>
.min-poper {
  min-width: 40px;
  background-color: rgba(255, 255, 255, 0);
  border: none;
}
#ruler-container {
  position: fixed;
  bottom: 0;
  left: 150px;
  right: 0;
  height: 20px;
  background-color: #000;
}
</style>
